<?php

namespace app\controller\api;

use app\core\ApiController;
use app\core\Request;
use app\service\BlessingOrderService;
use app\service\OrderService;
use app\service\ProductOrderService;
use app\core\Aop\AopClient;
use app\core\Aop\request\AlipayTradeAppPayRequest;

class Pay extends ApiController
{
    public function aliPay()
    {
        $member_id = Request::invoke()->getMember('member_id');
        $order_id = input('post.order_id');
        $info = OrderService::invoke()->getDetail($order_id, $member_id);
        if (!$info) {
            $this->response->error('订单不存在');
        }
        $aop = new AopClient;
        $aop->gatewayUrl = $this->app->config->get('app.alipay_url');
        $aop->appId = "app_id";
        $aop->rsaPrivateKey = $this->app->config->get('app.rsa_private_key');
        $aop->format = "json";
        $aop->charset = "UTF-8";
        $aop->signType = "RSA2";
        $aop->appId = $this->app->config->get('app.alipay_app_id');
        $aop->alipayrsaPublicKey = $this->app->config->get('app.alipay_rsa_public_key');
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        $request = new AlipayTradeAppPayRequest();
        $bizcontent = [
            "body" => '支付数据详情',
            "subject" => 'App支付',
            "out_trade_no" => $info['order_no'],
            "timeout_express" => '30m',
            "total_amount" => $info['price'],
            "product_code" => 'QUICK_MSECURITY_PAY',
        ];
//SDK已经封装掉了公共参数，这里只需要传入业务参数
        $bizcontent = json_encode($bizcontent);
        $request->setNotifyUrl("https://pray.veac.cn/api/notify/alinotice");
        $request->setBizContent($bizcontent);
//这里和普通的接口调用不同，使用的是sdkExecute
        $response = $aop->sdkExecute($request);
//htmlspecialchars是为了输出到页面时防止被浏览器将关键参数html转义，实际打印到日志以及http传输不会有这个问题
//        $data = ['response'=>$response];
        $this->response->success($response);
    }

    public function wxPay()
    {
        $member_id = Request::invoke()->getMember('member_id');
        $memberInfo = Request::invoke()->getMember();
        if (!$memberInfo['openid']) {
            $this->response->error('请授权登录');
        }
        $order_id = input('post.order_id');
        $order_type = input('post.order_type', 1);
        if ($order_type == 3) {
            $info = ProductOrderService::invoke()->info($order_id, $member_id);
        } else {
            $info = BlessingOrderService::invoke()->info($order_id, $member_id);
        }
        if (!$info) {
            $this->response->error('订单不存在');
        }
        // 当前时间
        $time = time();
        $openid = $memberInfo['openid'];
        $appid = $this->app->config->get('app.app_id');
        $mch_id = $this->app->config->get('app.mch_id');
        $apikey = $this->app->config->get('app.app_key');
        // 生成随机字符串
        $nonceStr = md5($time . $openid);
        // API参数
        $params = [
            'appid' => $appid,
            'attach' => json_encode(['order_type' => $info['order_type']]),
            'body' => '祈福订单',
            'mch_id' => $mch_id,
            'nonce_str' => $nonceStr,
            'notify_url' => 'https://pray.veac.cn/api/notify/notice',  // 异步通知地址
            'openid' => $openid,
            'out_trade_no' => $info['order_no'],
            'spbill_create_ip' => $_SERVER['REMOTE_ADDR'], //终端 IP
            'total_fee' => $info['price'] * 100, // 价格:单位分
            'trade_type' => 'JSAPI',
        ];
        // 生成签名
        $params['sign'] = $this->makeSign($params, $apikey);
        // 请求API
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        $result = $this->post($url, $this->toXml($params));
        $prepay = $this->xmlToArray($result);
        if ($prepay['return_code'] === 'FAIL') {
            $errMsg = "微信支付api：{$prepay['return_msg']}";
            $this->response->error($errMsg);
        }
        if ($prepay['result_code'] === 'FAIL') {
            $errMsg = "微信支付api：{$prepay['return_msg']}";
            $this->response->error($errMsg);
        }
        // 生成 nonce_str 供前端使用
        $paySign = $this->makePaySign($params['nonce_str'], $prepay['prepay_id'], $time, $apikey, $appid);
        $data = [
            'prepay_id' => $prepay['prepay_id'],
            'nonceStr' => $nonceStr,
            'timeStamp' => (string)$time,
            'paySign' => $paySign,
        ];
        $this->response->success($data);
    }

    public function wxApp()
    {
        $member_id = Request::invoke()->getMember('member_id');
        $order_id = input('post.order_id');
        $info = OrderService::invoke()->getDetail($order_id, $member_id);
        if (!$info) {
            $this->response->error('订单不存在');
        }
        // 当前时间
        $time = time();
        $appid = $this->app->config->get('app.wx_app_id');
        $mch_id = $this->app->config->get('app.wx_mch_id');
        $apikey = $this->app->config->get('app.app_key');
        // 生成随机字符串
        $nonceStr = md5($time . $member_id);
        // API参数
        $params = [
            'appid' => $appid,
            'attach' => json_encode(['order_type' => 4]),//4=app认购商品
            'body' => '祈福订单',
            'mch_id' => $mch_id,
            'nonce_str' => $nonceStr,
            'notify_url' => 'https://pray.veac.cn/api/notify/notice',  // 异步通知地址
            'out_trade_no' => $info['order_no'],
            'spbill_create_ip' => $_SERVER['REMOTE_ADDR'], //终端 IP
            'total_fee' => $info['price'] * 100, // 价格:单位分
            'trade_type' => 'APP',
        ];
        // 生成签名
        $params['sign'] = $this->makeSign($params, $apikey);
        // 请求API
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        $result = $this->post($url, $this->toXml($params));
        $prepay = $this->xmlToArray($result);
        if ($prepay['return_code'] === 'FAIL') {
            $errMsg = "微信支付api：{$prepay['return_msg']}";
            $this->response->error($errMsg);
        }
        if ($prepay['result_code'] === 'FAIL') {
            $errMsg = "微信支付api：{$prepay['return_msg']}";
            $this->response->error($errMsg);
        }
        // 生成 nonce_str 供前端使用
        $paySign = $this->makeAppSign($params['nonce_str'], $prepay['prepay_id'], $time, $apikey, $appid, $mch_id);
        $data = [
            'prepay_id' => $prepay['prepay_id'],
            'nonceStr' => $nonceStr,
            'timeStamp' => (string)$time,
            'paySign' => $paySign,
            'appId' => $appid,
            'mch_id' => $mch_id,
            'package' => 'Sign=WXPay',
        ];
        $this->response->success($data);
    }

    /**
     * 生成paySign
     * @param $nonceStr
     * @param $prepay_id
     * @param $timeStamp
     * @return string
     */
    private function makeAppSign($nonceStr, $prepay_id, $timeStamp, $apikey, $appId, $partnerId)
    {
        $data = [
            'appid' => $appId,
            'noncestr' => $nonceStr,
            'prepayid' => $prepay_id,
            'partnerid' => $partnerId,
            'package' => 'Sign=WXPay',
            'timestamp' => $timeStamp,
        ];
        // 签名步骤一：按字典序排序参数
        ksort($data);
        $string = $this->toUrlParams($data);
        // 签名步骤二：在string后加入KEY
        $string = $string . '&key=' . $apikey;
        // 签名步骤三：MD5加密
        $string = md5($string);
        // 签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    /**
     * 生成paySign
     * @param $nonceStr
     * @param $prepay_id
     * @param $timeStamp
     * @return string
     */
    private function makePaySign($nonceStr, $prepay_id, $timeStamp, $apikey, $appId)
    {
        $data = [
            'appId' => $appId,
            'nonceStr' => $nonceStr,
            'package' => 'prepay_id=' . $prepay_id,
            'signType' => 'MD5',
            'timeStamp' => $timeStamp,
        ];
        // 签名步骤一：按字典序排序参数
        ksort($data);
        $string = $this->toUrlParams($data);
        // 签名步骤二：在string后加入KEY
        $string = $string . '&key=' . $apikey;
        // 签名步骤三：MD5加密
        $string = md5($string);
        // 签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    /**
     * 输出xml字符
     * @param $values
     * @return bool|string
     */
    private function toXml($values)
    {
        if (!is_array($values)
            || count($values) <= 0
        ) {
            return false;
        }

        $xml = "<xml>";
        foreach ($values as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

    protected function post(string $url, $data = [], $useCert = false, $sslCert = [])
    {
        $header = [
            'Content-type: application/json;'
        ];
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_POST, TRUE);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        if ($useCert == true) {
            // 设置证书：cert 与 key 分别属于两个.pem文件
            curl_setopt($curl, CURLOPT_SSLCERTTYPE, 'PEM');
            curl_setopt($curl, CURLOPT_SSLCERT, $sslCert['certPem']);
            curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM');
            curl_setopt($curl, CURLOPT_SSLKEY, $sslCert['keyPem']);
        }
        $result = curl_exec($curl);
        curl_close($curl);
        return $result;
    }

    private function makeSign($values, $apikey)
    {
        //签名步骤一：按字典序排序参数
        ksort($values);
        $string = $this->toUrlParams($values);
        //签名步骤二：在string后加入KEY
        $string = $string . '&key=' . $apikey;
        //签名步骤三：MD5加密
        $string = md5($string);
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    /**
     * 格式化参数格式化成url参数
     * @param $values
     * @return string
     */
    private function toUrlParams($values)
    {
        $buff = '';
        foreach ($values as $k => $v) {
            if ($k != 'sign' && $v != '' && !is_array($v)) {
                $buff .= $k . '=' . $v . '&';
            }
        }
        return trim($buff, '&');
    }

    //xml 转换成数组
    private function xmlToArray($xml)
    {
        $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
        $val = json_decode(json_encode($xmlstring), true);
        return $val;
    }


}